package ija.controller;

import ija.controller.replay.ReplayMoves;
import ija.model.CheckersModel;
import ija.model.basis.Desk;
import ija.model.basis.Figure;
import ija.model.basis.Position;
import ija.model.history.HistoryMove;
import ija.view.MainWindow;
import ija.view.ReplayerMenuContent;
import ija.xls.notation.DeskCoordinate;
import ija.xls.notation.GameMove;
import ija.xls.notation.NotationReader;
import ija.xls.notation.NotationReaderFactory;
import ija.xls.notation.types.GameOperation;

import java.awt.FileDialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Iterator;

public class ReplayerController {

    protected ReplayerMenuContent view;
    protected CheckersModel model;
    protected NotationReader notation;
    protected ReplayState replayState;
    protected ReplayMoves replayMoves;
    protected MainWindow mainwindow;

    public enum ReplayState {
        PLAYING, STOPPED, PAUSED
    }

    // region Constructors
    public ReplayerController(MainWindow mw) {
        this.mainwindow = mw;
        this.setModel(new CheckersModel());
        this.notation = NotationReaderFactory.createNotationReader();
    }

    // endregion Constructors
    // region Setters
    public void setView(ReplayerMenuContent gc) {
        this.view = gc;
    }

    public void setModel(CheckersModel gm) {
        this.model = gm;
    }

    public void setState(ReplayState rs) {
        this.replayState = rs;
        this.getView().setState(rs);
    }

    public void setMessage(String message) {
        this.model.setMessage(message);
    }

    // endregion Setters
    // region Getters
    public NotationReader getNotationReader() {
        return this.notation;
    }

    public ReplayerMenuContent getView() {
        return this.view;
    }

    public CheckersModel getModel() {
        return this.model;
    }

    public ReplayState getState() {
        return this.replayState;
    }

    // endregion Getters
    public ReplayMoves parseMoves(String s, NotationReader nr) {
        ReplayMoves rm = new ReplayMoves();
        String[] lines = s.split("\n");
        int stopIndex = 0;
        HistoryMove hm = null;

        for (String line : lines) {
            hm = nr.translateRound(line);
            rm.addGameMove(hm.getFirstMove());
            rm.addGameMove(hm.getSecondMove());
            try {
                stopIndex = Integer.parseInt(this.getView().getStopAt());
            } catch (NumberFormatException e) {
                stopIndex = 0;
            }
            rm.setBreakpoint(stopIndex);
        }
        return rm;
    }

    // region Nested Classes
    public class Play implements ActionListener, Runnable {

        @Override
        public void actionPerformed(ActionEvent event) {
            new Thread(this).start();
        }

        @Override
        public void run() {
            ReplayerController rc = ReplayerController.this;
            GameMove gm = null;
            int time = 0;

            try {
                time = Integer.parseInt(rc.getView().getTime());
            } catch (NumberFormatException e) {
                rc.setMessage("Bad \"Interval:\" format. Only numbers are accepted.");
            }

            if (rc.getState() == ReplayState.STOPPED) {
                NotationReader nr = rc.getNotationReader();
                rc.replayMoves = rc.parseMoves(rc.getView().getMoveHistory(), nr);
            }
            rc.setState(ReplayState.PLAYING);

            while ((gm = rc.replayMoves.getNextMove()) != null) {
                try {
                    rc.getView().selectLine(rc.replayMoves.getRound());
                    for (int t = 0; t < time; t++) {
                        rc.setMessage("Round " + rc.replayMoves.getRound() + ": Next move in "
                                + (time - t) + " seconds.");
                        Thread.sleep(1000);
                    }
                    rc.getModel().move(gm);

                } catch (InterruptedException e) {
                    System.err.println("Play thread interrupted!");
                }

                if (rc.getState() != ReplayState.PLAYING) {
                    return;
                }
            }
            rc.setMessage("No more moves.");
            rc.setState(ReplayState.PAUSED);
        }
    }

    public class Pause implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            ReplayerController rc = ReplayerController.this;
            rc.setState(ReplayState.PAUSED);
        }
    }

    public class Stop implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            ReplayerController rc = ReplayerController.this;
            rc.setState(ReplayState.STOPPED);
            rc.createFirstListener().actionPerformed(null);
        }
    }

    public class First implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            ReplayerController rc = ReplayerController.this;
            rc.model.initGame(rc.model.getTopPlayer().getColor());
            if (rc.replayMoves != null) {
                rc.replayMoves.setIndex(0);
                rc.getView().selectLine(rc.replayMoves.getRound());
            }
        }
    }

    public class Last implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            ReplayerController rc = ReplayerController.this;
            String time = rc.getView().getTime();
            rc.getView().setTime("0");
            rc.createPlayListener().actionPerformed(null);
            rc.getView().setTime(time);
            rc.getView().selectLine(rc.replayMoves.getRound());
        }
    }

    public class Next implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            ReplayerController rc = ReplayerController.this;
            GameMove gm = null;

            if (rc.getState() == ReplayState.STOPPED) {
                NotationReader nr = rc.getNotationReader();
                rc.replayMoves = rc.parseMoves(rc.getView().getMoveHistory(), nr);
            }
            rc.setState(ReplayState.PAUSED);

            gm = rc.replayMoves.getNextMove();

            if (gm != null) {
                rc.getModel().move(gm);
                rc.getView().selectLine(rc.replayMoves.getRound());
                rc.setMessage("Round " + rc.replayMoves.getRound());
            }
        }
    }

    public class Previous implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            ReplayerController rc = ReplayerController.this;
            Desk desk = rc.getModel().getDesk();
            GameMove gm = null;
            GameOperation go = null;
            DeskCoordinate dcFrom, dcTo = null;
            Position pFrom, pTo = null;
            Figure fToMove, fToRevive = null;

            if (rc.getState() == ReplayState.STOPPED) {
                System.err.println("Player is stopped!!!");
                return;
            }

            gm = rc.replayMoves.getPreviousMove();

            if (gm == null) {
                System.err.println("No previous move!!!");
                return;
            }

            go = gm.getOperation();
            gm = gm.revert();
            Iterator<DeskCoordinate> dcI = gm.iterator();

            if (dcI.hasNext()) {
                dcFrom = dcI.next();
                pFrom = desk.getPositionAt(dcFrom);
                fToMove = desk.getFigureAt(dcFrom);
            } else {
                System.err.println("Desk Coords incomplete!!!");
                return;
            }

            switch (go) {
            case JUMPSET:
                fToMove = rc.getModel().unsetQueen(pFrom);
            case JUMP:
                while (dcI.hasNext()) {
                    dcTo = dcI.next();
                    pTo = desk.getPositionAt(dcTo);
                    fToMove.uncheckedMove(pTo);
                    fToRevive = desk.popRemovedFigure();
                    if (fToRevive == null) {
                        System.err.println("NothingToRevive!!!");
                    } else {
                        fToRevive.revive();
                    }
                    dcFrom = dcTo;
                }
                break;
            case MOVESET:
                fToMove = rc.getModel().unsetQueen(pFrom);
            case MOVE:
                dcTo = dcI.next();
                pTo = desk.getPositionAt(dcTo);
                fToMove.uncheckedMove(pTo);
                break;
            case NOP:
                break;
            default:
                break;
            }
            rc.getView().setFromDesk(desk);
            rc.getView().selectLine(rc.replayMoves.getRound());
        }
    }

    public class LoadGame implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            String filePath;
            FileDialog dLoad;
            ReplayerController rc = ReplayerController.this;
            dLoad = new FileDialog(rc.mainwindow, "Load XML file ...", FileDialog.LOAD);

            dLoad.setFile("*.xml");

            dLoad.setFilenameFilter(new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    return name.endsWith(".xml");
                }
            });

            dLoad.setVisible(true);

            if ((dLoad.getDirectory() != null) && (dLoad.getFile() != null)) {
                filePath = dLoad.getDirectory() + dLoad.getFile();
                if (filePath.endsWith(".xml")) {
                    rc.view.setHistoryLoaded(false);
                    rc.model.initInputXml(filePath);
                    rc.model.initHistoryXml();
                    rc.model.initPlayersXml();
                    rc.createStopListener().actionPerformed(null);
                }
            }

        }
    }

    // endregion Nested Classes

    // region Factories
    public LoadGame createLoadListener() {
        return new LoadGame();
    }

    public Play createPlayListener() {
        return new Play();
    }

    public Pause createPauseListener() {
        return new Pause();
    }

    public Stop createStopListener() {
        return new Stop();
    }

    public First createFirstListener() {
        return new First();
    }

    public Last createLastListener() {
        return new Last();
    }

    public Previous createPreviousListener() {
        return new Previous();
    }

    public Next createNextListener() {
        return new Next();
    }
    // endregion Factories
}
